using System.Collections.Generic;
using System;

using Tao.OpenGl;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
using System.Drawing;

namespace cs5643.rigidbody
{

    /**
     * Jelly Bean Factory implementation. Creates a pinned rigid
     * container, and shoots in numerous jelly beans---yum.  Estimates the
     * number of beans at rest, and the maximum height of resting beans.
     * Try to get as many beans into the jar as possible, and have them
     * behave passively.  FREEZING THE POSITIONS OF THE BEANS IS NOT
     * ALLOWED HERE---use friction and your projected Gauss-Seidel solver
     * method(s). See if you can get >256 beans stacking stably and
     * achieve OCD distinction!
     * 
     * YOU SHOULD NOT NEED TO EDIT THIS CODE IN THE ASSIGNMENT ---
     * MODIFICATIONS MAY VOID YOUR Jelly-Bean-Factory SUBMISSION.
     *
     * @author Doug James, March 2009.
     */
    public class JellyBeanFactory
    {
        private int nOCD = 256;
        private bool OCD = false;

        private double time;

        /// Time of last bean emission
        private double tLast;

        List<RigidBody> addedBeans = new List<RigidBody>();//added beans

        RigidBodySystem RBS;

        /** Beans of different color/type for cloning */
        List<RigidBody> beanTypes = new List<RigidBody>();

        private Random random = new Random(2341234);

        //private TextRenderer textRenderer;
        private Font font;

        /** Times the simulation. */
        private WallClock wallClock;

        private RestingBeans restingBeans = null;

        /** Creates an empty jelly bean container/boundary, and prepares
         * for jelly bean emission. */
        public JellyBeanFactory(RigidBodySystem RBS)
        {
            Console.Out.WriteLine("##### JELLY BEAN FACTORY #####");

            wallClock = new WallClock();

            this.RBS = RBS;
            font = new Font("Monospaced", 16, FontStyle.Bold);
            //textRenderer = new TextRenderer(); //SansSerif ,,, Dialog

            /// Find non-jar rigid bodies for future jelly bean cloning:
            foreach (RigidBody b in RBS.getRigidBodies())
            {
                if (b.getNBlocks() < 1000)
                {//not jar --> a bean!
                    beanTypes.Add(b);
                }
            }

            reset();
        }

        public class WallClock
        {
            long t0 = System.DateTime.Now.Ticks;
            public double timeMin()
            {
                double min = (System.DateTime.Now.Ticks - t0) * 0.001 / 60.0f;
                int min100 = (int)(min * 100);
                return min100 / 100.0f;
            }
        }

        /** Deletes all previous jelly beans, and restarts factory. */
        public void reset()
        {
            /// REMOVE BEANS ...
            foreach (RigidBody b in addedBeans) RBS.remove(b);
            addedBeans.Clear();
            RBS.reset();

            /// RESETS BODIES (RANDOMIZES ANG VELOCITY)
            foreach (RigidBody b in RBS.getRigidBodies())
            {
                if (!b.isPinned())
                {
                    b.v.y = -2;
                    b.omega = 100.0 * (random.NextDouble() - 0.5);
                }
            }

            time = 0;
            tLast = -1;

            restingBeans = new RestingBeans(RBS);
            wallClock = new WallClock();///reset time
        }

        /** Creates any new jelly beans, but does not advance
         * positions---done elsewhere by integrator. */
        public void advanceTime(double dt)
        {
            if (time - tLast > 0.01)
            {/// MAKE NEW PARTICLE:

                RigidBody cloneMe = beanTypes[random.Next(beanTypes.Count)];
                RigidBody body = new RigidBody(cloneMe);

                double xCoord = 0.45 + 0.05 * (RBS.getNBodies() % 3);

                body.x0 = new Pnt2D(xCoord, 1);
                body.reset();
                body.theta = 2.0 * Math.PI * random.NextDouble();
                body.omega = 10.0 * (random.NextDouble() - 0.5);
                body.v = new Pnt2D(random.NextDouble() - 0.5, -2);

                RBS.add(body);
                addedBeans.Add(body);

                tLast = time;
            }

            time += dt;

            restingBeans.update();
        }

        /** Number of simulated jelly beans. */
        public int getNJellyBeans()
        {
            return (RBS.getNBodies() - 1);
        }


        /** Displays spaghetti text stats. */
        public void display( int width, int height)
        {
            //TODO fix drawing text, draw on texture then show texture, see FormExample2 line 21 onwards
            //textRenderer.beginRendering(width, height);
            //{
            //    // optionally set the color
            //    textRenderer.setColor(0.0f, 0.0f, 0.0f, 1f);

            //    // BUG: Text position should be in relative coords for resizing
            //    textRenderer.draw("Jelly Beans: " + getNJellyBeans(), 24, height - 34);
            //    textRenderer.draw("Bean Timer : " + (float)((int)(10000.0f * (time)) / 10000.0f) + " sec", 24, height - 54);
            //    textRenderer.draw("Wall Timer : " + (float)wallClock.timeMin() + " min", 24, height - 74);
            //    textRenderer.draw("#Resting   : " + restingBeans.getNRest(), 24, height - 94);
            //    textRenderer.draw("#RestingMax: " + restingBeans.getNRestMax(), 24, height - 114);
            //    textRenderer.draw("Pile Height: " + (float)restingBeans.getHeight(), 24, height - 134);
            //    textRenderer.draw("Best Height: " + (float)restingBeans.getBestHeight(), 24, height - 154);

            //    if (restingBeans.getNRestMax() > nOCD) OCD = true;

            //    if (OCD)
            //    {
            //        textRenderer.draw("OCD Distinction!  (>" + nOCD + " Resting)", 24, height - 174);
            //    }
            //}
            //textRenderer.endRendering();

            restingBeans.display();

             //DRAW LINE AT HIGHEST HEIGHT:
            Gl.glColor3d(0.2f, 0.2f, 0.2f);
            Gl.glBegin(Gl.GL_LINES);
            Gl.glVertex3d(0.90, restingBeans.getBestHeight(),0);
            Gl.glVertex3d(1, restingBeans.getBestHeight(),0);
            Gl.glEnd();
        }

        /** 
         * Estimates how many beans are at rest, and their maximum height,
         * which is an indirect test of the solver's friction ability. 
         */
        class RestingBeans
        {
            private RigidBody highest = null;
            private double height = 0;
            private double bestHeight = 0;

            private int nRest = 0;
            private int nRestMax = 0;
            RigidBodySystem RBS;

            public RestingBeans(RigidBodySystem RBS) {
                this.RBS = RBS;
            }

            [MethodImpl(MethodImplOptions.Synchronized)]
            public void update()
            {
                nRest = 0;
                highest = null;
                height = 0;

                foreach (RigidBody b in RBS.getRigidBodies())
                {

                    if (b.getNBlocks() > 1000) continue;// IGNORE JAR

                    if (atRest(b))
                    {
                        nRest++;

                        /// RECORD MAX RESTING HEIGHT:
                        double h = b.getPosition().y;
                        if (h > height)
                        {
                            height = h;
                            highest = b;
                        }
                    }
                }

                if (nRest > nRestMax)
                    nRestMax = nRest;

                if (height > bestHeight)
                    bestHeight = height;
            }

            /** Returns true if bean is at rest based on a mass-normalized
             * kinetic energy metric. */
            public bool atRest(RigidBody b)
            {
                double meanVel = Math.Sqrt(b.getKineticEnergy() / b.getMass());
                return (meanVel < 0.01);
            }

            [MethodImpl(MethodImplOptions.Synchronized)]
            public int getNRest() { return nRest; }
            [MethodImpl(MethodImplOptions.Synchronized)]
            public int getNRestMax() { return nRestMax; }
            [MethodImpl(MethodImplOptions.Synchronized)]
            public double getBestHeight() { return bestHeight; }
            [MethodImpl(MethodImplOptions.Synchronized)]
            public double getHeight() { return height; }

            [MethodImpl(MethodImplOptions.Synchronized)]
            public RigidBody getHighestRestingBean() { return highest; }

            /** Draws a white dot on each bean at rest. */
            public void display()
            {
                foreach (RigidBody b in RBS.getRigidBodies())
                {
                    if (b.getNBlocks() > 1000) continue;// IGNORE JAR
                    if (atRest(b))
                    {
                        Gl.glColor3d(0, 0, 0);
                        Disk.displayDisk( b.getPosition(), 0.008);
                        Gl.glColor3d(1, 1, 1);
                        Disk.displayDisk( b.getPosition(), 0.006);
                    }
                }
            }
        }


    }
}